package LAAMI;

import java.util.ArrayList;
import java.util.Iterator;

import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;

import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ScheduledMethod;

public class OptionMarket {

	private double price;
	private double maxCall;
	private double minAsk;
	// Black-Scholes model prices (theoretical prices)
	private double BSprice;
	// Pricing bias or pricing error, which is defined as market
	// price/theoretical price
	private double pricingBias;
	// Pricing Bias List
	private static ArrayList<Double> pbList;
	private double pbMean, pbMin, pbMax, pbKurtosis, pbSkewness, pbStDev;
	private double maturity;

	public OptionMarket(double optionPrice, double maturity) {
		setPrice(0);
		pbList = new ArrayList<Double>();
		setMaturity(maturity);
	}

	// In every step, it's the second method to be executed.
	// We produce 5 different option market prices, each one calculated as the
	// average of the minAsk, maxCall produced for the specific strike price
	@ScheduledMethod(start = 52, interval = 1, priority = 2)
	public void step() {

		calculateBSprice();
		setMaxCall(findMaxCall());
		setMinAsk(findMinAsk());

		if (maxCall >= minAsk)
			setPrice((double) (maxCall + minAsk) / 2);
		else
			System.out.println("No trading.");

		double pbAver = (double) price / BSprice;
		setPricingBias(pbAver);
		pbList.add((Double) pbAver);

		double day = RunEnvironment.getInstance().getCurrentSchedule()
				.getTickCount();
		if (day == maturity * 252 + 50) {
			DescriptiveStatistics stats = new DescriptiveStatistics();
			for (int i = 0; i < pbList.size(); i++)
				stats.addValue(pbList.get(i));
			pbMean = stats.getMean();
			pbMax = stats.getMax();
			pbMin = stats.getMin();
			pbKurtosis = stats.getKurtosis();
			pbSkewness = stats.getSkewness();
			pbStDev = stats.getStandardDeviation();
		}

	}

	public double findMaxCall() {
		double maxCall = Double.MIN_VALUE;
		Iterator<Investor> iterator = LAAMIBuilder.getAgentList().iterator();
		double callPrice;
		while (iterator.hasNext()) {
			callPrice = iterator.next().getCallPrice();
			if (callPrice > maxCall)
				maxCall = callPrice;
		}
		return maxCall;
	}

	public double findMinAsk() {
		double minAsk = Double.MAX_VALUE;
		Iterator<Investor> iterator = LAAMIBuilder.getAgentList().iterator();
		double askPrice;
		while (iterator.hasNext()) {
			askPrice = iterator.next().getAskPrice();
			if (askPrice < minAsk)
				minAsk = askPrice;
		}
		return minAsk;
	}

	// We calculate the price from Black-Scholes model with known volatility
	public void calculateBSprice() {
		double stockPrice = LAAMIBuilder.getStockmarket().getPrice();
		double strikePrice = LAAMIBuilder.getStockmarket().getStrikePrice();
		double volatility = LAAMIBuilder.getStockmarket().getVolatility();
		boolean call = true;
		setBSprice(BlackScholes.getPrice(call, stockPrice, strikePrice,
				volatility));
	}

	/* -------------setters/getters-------------------- */
	public double getMaxCall() {
		return maxCall;
	}

	public void setMaxCall(double m) {
		maxCall = m;
	}

	public double getMinAsk() {
		return minAsk;
	}

	public void setMinAsk(double m) {
		minAsk = m;
	}

	public double getPrice() {
		return price;
	}

	public void setPrice(double p) {
		price = p;
	}

	public void setBSprice(double s) {
		BSprice = s;
	}

	public double getBSprice() {
		return BSprice;
	}

	public double getPricingBias() {
		return pricingBias;
	}

	public void setPricingBias(double p) {
		pricingBias = p;
	}

	public double getPbMean() {
		return pbMean;
	}

	public void setPbMean(double p) {
		pbMean = p;
	}

	public double getPbMin() {
		return pbMin;
	}

	public void setPbMin(double pbMin) {
		this.pbMin = pbMin;
	}

	public double getPbMax() {
		return pbMax;
	}

	public void setPbMax(double pbMax) {
		this.pbMax = pbMax;
	}

	public double getPbKurtosis() {
		return pbKurtosis;
	}

	public void setPbKurtosis(double pbKurtosis) {
		this.pbKurtosis = pbKurtosis;
	}

	public double getPbSkewness() {
		return pbSkewness;
	}

	public void setPbSkewness(double pbSkewness) {
		this.pbSkewness = pbSkewness;
	}

	public double getPbStDev() {
		return pbStDev;
	}

	public void setPbStDev(double pbStDev) {
		this.pbStDev = pbStDev;
	}

	public double getMaturity() {
		return maturity;
	}

	public void setMaturity(double maturity) {
		this.maturity = maturity;
	}
}
